﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using iPal.analysis;
using iPal.lexer;
using iPal.node;
using iPal.parser;

namespace GOLD_TEST
{

    public class SemanticException : ApplicationException
    {
        public SemanticException(String message)
            : base(message)
        {
        }
    }

    class SemanticAnalyser : DepthFirstAdapter
    {
        private Hashtable _symbolTable = new Hashtable();

        public Hashtable SymbolTable { get { return _symbolTable; } }


        public override void OutAAssignVarAssign(AAssignVarAssign node)
        {
            string varId = node.GetId().Text;

            if (!_symbolTable.Contains(varId))
            {
                _symbolTable.Add(varId, node.GetType().ToString());
            }
            else
            {
                throw new SemanticException(varId + " already exsists.");
            }

            var list = printRoot(node.GetExpr());

            string curr = node.GetType().ToString().Trim();
            foreach (Node n in list)
            {
                string type = n.GetType().ToString();
                if (type.Contains("literal") || type.Contains("TId"))
                {
                    if (type.Equals("iPal.node.TId"))
                    {
                        TId id = (TId)n;
                        type = _symbolTable[id.Text].ToString().Trim();
                    }
                    typeCheckSwitch(type, curr);
                }
                    
            }
        }

        public override void OutAExprVarAssign(AExprVarAssign node)
        {

            var list = printRoot(node.GetExpr());

            string curr = _symbolTable[list[0].ToString().Trim()].ToString().Trim();
            foreach (Node n in list)
            {
                string type = n.GetType().ToString();
                if (type.Contains("literal") || type.Contains("TId"))
                {
                    if (type.Equals("iPal.node.TId"))
                    {
                        TId id = (TId)n;
                        type = _symbolTable[id.Text].ToString().Trim();
                    }
                    typeCheckSwitch(type, curr);
                }

            }
        }

        public override void OutADeclVarAssign(ADeclVarAssign node)
        {
            string id = node.GetId().Text;

            if (!_symbolTable.Contains(id))
            {
                _symbolTable.Add(id, node.GetType().ToString());
            }
            else
            {
                throw new SemanticException(id + " already exsists.");
            }


        }

        public override void OutAIdentifierValue(AIdentifierValue node)
        {

            string id = node.GetId().Text;

            if (!_symbolTable.Contains(id))
            {
                throw new SemanticException(id + " hasnt been declared before use");
            }
        }

        public override void OutADecValue(ADecValue node)
        {
            
        }

        public override void OutAPlusOpAdd(APlusOpAdd node)
        {
            
            var list = printRoot(node);
            Console.WriteLine("****");

            foreach (Node n in list) Console.WriteLine(n.GetType().ToString());
            Console.WriteLine("****");
        }


        #region HelperMethods

        private void operatorCheckSwitch(string type, string curr)
        {

        }

        private void typeCheckSwitch(string type, string curr)
        {
            switch (curr)
            {
                case "int":
                    if (!type.Contains("Dec") && !type.Equals("int"))
                        throw new SemanticException("Cannot convert from: " + type + " to a: " + curr);
                    break;
                case "float":
                    if (!type.Contains("Dec") && !type.ToLower().Contains("float") && !type.Equals("int"))
                        throw new SemanticException("Cannot convert from: " + type + " to a: " + curr);
                    break;
                case "string":
                    if (!type.Contains("String"))
                        throw new SemanticException("Cannot convert from: " + type + " to a: " + curr);
                    break;
                case "bool":
                    if (!type.Contains("Bool"))
                        throw new SemanticException("Cannot convert from: " + type + " to a: " + curr);
                    break;
            }
        }

        private List<Node> printRoot(Node node)
        {
            List<Node> list = new List<Node>();
            printNode(node, ref list);
            return list;
        }

        private void printNode(Node node, ref List<Node> list)
        {
            if (node.ChildList.Count == 0) //checks if node is leaf ie. has no children
            {
                list.Add(node);
            }
            foreach (Node child in node.ChildList)
            {
                printNode(child, ref list);
            }
        }

        #endregion


    }
}
